home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.qualcomm.com
/
2014.06.ftp.qualcomm.com.tar
/
ftp.qualcomm.com
/
eudora
/
developers
/
emsapi
/
carbon_emsapi.sit.hqx
/
Macintosh API Support
/
rfc1847.c
< prev
next >
Wrap
Text File
|
2001-03-08
|
18KB
|
637 lines
/* ======================================================================
Functions to aid manipulation and parsing of MIME RFC1847 objects.
Filename: rfc1847.c
Last Edited: March 7, 1997
Authors: Scott Manjourides, Bob Fronabarger
Copyright: 1995, 1996 QUALCOMM Inc.
Technical support: <emsapi-info@qualcomm.com>
*/
#include "emsapi-mac.h"
#include "rfc822.h"
#include "rfc1847.h"
#include "mimetype.h"
#include "copycat.h"
static StringPtr kBoundaryParameterName = "\pboundary";
static StringPtr kBoundaryPrefixSuffix = "\p=-=-=-=";
static void RFC1847_CreateInit(createStateHandle pState);
static long RFC1847_ParseInit(parseStateHandle pState);
/* =========================================================================
* Allocates and returns a pointer to an initialized createState structure.
*
* NOTE: The user of this function is responsible for properly
* deleting the returned data by calling DeleteCreateState()
* when the state data is no longer needed.
*
* No arguments.
*
* Returns handle to newly created createState structure. nil if error.
*/
createStateHandle NewCreateState(void)
{
createStatePtr p, *pH = nil;
pH = (createStateHandle) NewHandle(sizeof(createState));
if (pH) {
HLock((Handle) pH);
p = *pH;
p->initialized = false;
p->stage = p->next_stage = p->afterout_stage = CSE_Fail;
p->current_part = 0;
p->hTmpBuf = Make_Buf();
p->hOutputBuf = nil;
p->boundaryStr[0] = 0;
HUnlock((Handle) pH);
}
return pH;
}
/* =========================================================================
* Frees all memory within the state structure, including the structure
* itself.
*
* NOTE: The pointer argument should be assumed invalid after this call.
*
* Args:
* pState [IN] Pointer to the state structure to be deleted.
*/
void DeleteCreateState(createStateHandle p)
{
if (p) {
Delete_Buf((**p).hTmpBuf);
DisposeHandle((Handle)p);
}
}
/* =========================================================================
* Allocates and returns a pointer to an initialized parseState structure.
*
* NOTE: The user of this function is responsible for properly
* deleting the returned data by calling DeleteCreateState()
* when the state data is no longer needed.
*
* No arguments.
*
* Returns pointer to newly created parseState structure. nil if error.
*/
parseStateHandle NewParseState()
{
parseStatePtr p, *pH;
pH = (parseStateHandle) NewHandle(sizeof(parseState));
if (!pH)
return (nil);
HLock((Handle)pH);
p = *pH;
p->initialized = false;
p->stage = p->next_stage = p->afterout_stage = PSE_Fail;
p->current_part = p->outputCount = p->afteroutSkip = p->leftSpanCount = 0;
p->hOutputBuf = p->hSearchBuf = nil;
p->hPrevBuf = Make_Buf();
p->hBoundaryBuf = Make_Buf();
p->hDblNewlineBuf = Make_Buf();
p->hNewlineBuf = Make_Buf();
HUnlock((Handle)pH);
return (pH);
}
/* =========================================================================
* Frees all memory within the state structure, including the structure
* itself.
*
* NOTE: The pointer argument should be assumed invalid after this call.
*
* Args:
* pState [IN] Pointer to the state structure to be deleted.
*/
void DeleteParseState(parseStateHandle p)
{
if (!p) {
Delete_Buf((**p).hPrevBuf);
Delete_Buf((**p).hBoundaryBuf);
Delete_Buf((**p).hDblNewlineBuf);
Delete_Buf((**p).hNewlineBuf);
DisposeHandle((Handle) p);
}
}
#pragma mark -
/* =========================================================================
* Performs necessary initialization for RFC1847_Create().
*
* Args:
* pState [IN] Pointer to the state structure.
*
* Return value: BOOLEAN indicating success.
*/
static void RFC1847_CreateInit(createStateHandle pState)
{
Str31 numStr;
Str255 theStr;
short n;
CopyPP(kBoundaryPrefixSuffix, theStr);
n = Random();
if (n < 0)
n = -n;
NumToString(n, numStr);
CatPP(numStr, theStr);
n = Random();
if (n < 0)
n = -n;
NumToString(n, numStr);
CatPP(numStr, theStr);
n = Random();
if (n < 0)
n = -n;
NumToString(n, numStr);
CatPP(numStr, theStr);
CatPP(kBoundaryPrefixSuffix, theStr);
CopyPP(theStr, (**pState).boundaryStr);
(**pState).stage = CSE_Start;
(**pState).initialized = true;
}
/* =========================================================================
* Creates RFC1847 MIME structure.
*
* Args:
* mimeHdl [IN] MIME content info, used to create the MIME headers.
* pOutBuf [OUT] Output buffer (RFC1847 MIME structure).
* pInPart1 [IN] Input buffer used for first part.
* pInPart2 [IN] Input buffer used for second part.
* pState [IN/OUT] Pointer to state info, caller must preserve.
*
* Return values:
* RFC1847_COMPLETED Input was sucessfully and completely parsed.
* RFC1847_BUFFERFULL The output buffer is full; input partially parsed.
* RFC1847_FAIL Parsing error.
*/
long RFC1847_Create(emsMIMEtypeH mimeHdl, BufTypeHandle pOutBuf,
BufTypeHandle pInPart1, BufTypeHandle pInPart2, createStateHandle hState)
{
Handle hCT = nil;
createStatePtr pState;
long status = RFC1847_COMPLETED;
unsigned long nLen, nOut;
if (!(**hState).initialized)
RFC1847_CreateInit(hState);
HLock((Handle) hState);
pState = *hState;
while (pState->stage != CSE_Done) {
switch (pState->stage) {
case CSE_Start:
pState->stage = CSE_DoHeader; /* Just an entry point */
break;
case CSE_DoHeader:
pState->stage = CSE_Fail; // If not changed, then something failed
if (mimeHdl && pState->boundaryStr) {
/* Remove the boundary parameter from the MIME struct -- if it's there */
RemoveMimeParameter(mimeHdl, kBoundaryParameterName);
/* Add the boundary to the MIME type struct */
AddMimeParameter(mimeHdl, kBoundaryParameterName, pState->boundaryStr);
hCT = StringMimeType(mimeHdl);
/* Create an RFC822 Content-Type header line from the MIME struct*/
if (hCT != nil) {
EmptyBuf_Buf(pState->hTmpBuf);
HandleCat_Buf(pState->hTmpBuf, hCT); // Content-Type:
StrCat_Buf(pState->hTmpBuf, "\r\n");
ResetPos_Buf(pState->hTmpBuf);
DisposeHandle(hCT);
pState->hOutputBuf = pState->hTmpBuf;
pState->afterout_stage = CSE_DoBoundary;
pState->stage = CSE_DoOutput;
}
}
break;
case CSE_DoBoundary:
StrCpy_Buf(pState->hTmpBuf, "\p\r\n");
StrCat_Buf(pState->hTmpBuf, "--");
PtoCstr(pState->boundaryStr);
StrCat_Buf(pState->hTmpBuf, (char*) pState->boundaryStr);
CtoPstr((char*) pState->boundaryStr);
if ((pState->current_part) < 2) { /* End the header and first part */
pState->current_part++;
pState->afterout_stage = CSE_DoPart;
}
else { /* End of second part */
StrCat_Buf(pState->hTmpBuf, "--");
pState->afterout_stage = CSE_Done;
}
StrCat_Buf(pState->hTmpBuf, "\r\n");
ResetPos_Buf(pState->hTmpBuf);
pState->stage = CSE_DoOutput;
pState->hOutputBuf = pState->hTmpBuf;
break;
case CSE_DoPart:
switch (pState->current_part) {
case 1: /* We're doing part 1 now */
if (PosLen_Buf(pInPart1) > 0) {
pState->stage = CSE_DoOutput;
pState->hOutputBuf = pInPart1;
pState->afterout_stage = pInPart2 ? CSE_DoBoundary : CSE_DoPart;
}
else { /* pInPart1 is empty/completed/non-existant */
if (pInPart2)
pState->stage = CSE_DoBoundary;
else /* !pInPart2 */
goto Exit; /* We've eaten all of part 1, and there is no part 2 */
}
break;
case 2: /* We're doing part 2 now */
if (PosLen_Buf(pInPart2) > 0) {
pState->stage = CSE_DoOutput;
pState->hOutputBuf = pInPart2;
pState->afterout_stage = CSE_DoPart;
}
else { /* pInPart1 is empty/completed/non-existant */
if (pInPart2)
goto Exit; /* We've eaten all of part 2 */
else /* !pInPart2 */
pState->stage = CSE_DoBoundary;
}
break;
}
break;
case CSE_DoOutput:
if (pOutBuf) {
nLen = PosLen_Buf(pState->hOutputBuf);
nOut = BufIns_Buf(pOutBuf, pState->hOutputBuf);
if (nOut < nLen) { /* Anything left to output */
status = RFC1847_BUFFERFULL;
goto Exit;
}
pState->stage = pState->afterout_stage;
}
else {
status = RFC1847_BUFFERFULL;
goto Exit;
}
break;
case CSE_Fail:
status = RFC1847_FAIL;
goto Exit;
} /* switch (stage) */
} /* while */
Exit:
HUnlock((Handle) hState);
return status;
}
/* =========================================================================
* Performs necessary initialization for RFC1847_Parse().
*
* Args:
* hState [IN] Pointer to the state structure.
*
* Return value: BOOLEAN indicating success.
*/
static long RFC1847_ParseInit(parseStateHandle hState)
{
if (!hState)
return (false);
StrCpy_Buf((**hState).hNewlineBuf, "\p\r\n");
ResetPos_Buf((**hState).hNewlineBuf);
StrCpy_Buf((**hState).hDblNewlineBuf, "\p\r\n\r\n");
ResetPos_Buf((**hState).hDblNewlineBuf);
(**hState).stage = PSE_Start;
(**hState).initialized = true;
return (true);
}
/* =========================================================================
* Parses RFC1847 MIME structure, returning MIME info and separated parts.
*
* Args:
* mimeHdl [OUT] Returns a pointer to a newly created emsMIMEtype.
* pOutPart1 [OUT] Output buffer, part 1.
* pOutPart2 [OUT] Output buffer, part 2.
* pInBuf [IN] Input buffer containing full RFC1847 MIME structure.
* hState [IN/OUT] Pointer to state info, caller must preserve.
*
* Return values:
* RFC1847_COMPLETED Input was sucessfully and completely parsed.
* RFC1847_BUFFERFULL The output buffer is full; input partially parsed.
* RFC1847_FAIL Parsing error.
*/
long RFC1847_Parse(emsMIMEtypeH *mimeHdl, BufTypeHandle pOutPart1,
BufTypeHandle pOutPart2, BufTypeHandle pInBuf, parseStateHandle hState)
{
Boolean bForceJumpState = false;
Handle cp;
unsigned long nSkip, nRemain, nPrevLen, nOut;
long status = RFC1847_COMPLETED;
Ptr pHeader, bufP;
char *pCT;
parseStatePtr pState;
BufTypeHandle p;
HLock((Handle) hState);
pState = *hState;
if (!pState->initialized)
if (!RFC1847_ParseInit(hState))
pState->stage = PSE_Fail;
while ((bForceJumpState) || (PosLen_Buf(pInBuf) > 0)) {
bForceJumpState = false;
switch (pState->stage) {
case PSE_Start: /* Just an entry point */
pState->stage = PSE_SkipHeader;
break;
case PSE_SkipHeader:
pState->stage = PSE_KeepUntil;
pState->next_stage = PSE_ParseHeader;
pState->hSearchBuf = pState->hDblNewlineBuf;
break;
case PSE_ParseHeader: /* Do all the mimeHdl stuff */
ResetPos_Buf(pState->hPrevBuf);
nPrevLen = PosLen_Buf(pState->hPrevBuf);
pHeader = NewPtr(nPrevLen + 1); // gethandlepos_buf(pState->hPrevBuf);
if (!pHeader) {
pState->stage = PSE_Fail;
bForceJumpState = true;
continue;
}
bufP = GetPos_Buf(pState->hPrevBuf);
BlockMoveData(bufP, pHeader, nPrevLen);
*(pHeader + nPrevLen) = '\0';
pCT = RFC822_ExtractHeader(pHeader, "Content-Type:");
DisposePtr(pHeader);
if (!pCT) {
pState->stage = PSE_Fail;
bForceJumpState = TRUE;
continue;
}
*mimeHdl = ParseMakeMimeType(pCT);
DisposePtr(pCT);
/* get boundary info */
cp = GetMimeParameter(*mimeHdl, kBoundaryParameterName);
if (!cp) {
pState->stage = PSE_Fail;
bForceJumpState = true;
continue;
}
StrCpy_Buf(pState->hBoundaryBuf, "\p\r\n--");
HandleCat_Buf(pState->hBoundaryBuf, cp);
// DO NOT free() cp
ResetPos_Buf(pState->hBoundaryBuf);
nSkip = SkipCount_Buf(pState->hPrevBuf, pState->hBoundaryBuf);
nRemain = nPrevLen - nSkip;
pState->leftSpanCount = nRemain;
Free_Buf(pState->hPrevBuf);
pState->stage = PSE_SkipFirst;
break;
case PSE_SkipFirst:
pState->stage = PSE_IgnoreUntil;
pState->hSearchBuf = pState->hBoundaryBuf;
pState->next_stage = PSE_PrePart;
break;
case PSE_PrePart:
pState->stage = PSE_IgnoreUntil;
pState->hSearchBuf = pState->hNewlineBuf;
pState->next_stage = PSE_OutPart;
break;
case PSE_OutPart:
pState->current_part++;
if (pState->current_part <= 2) {
pState->stage = PSE_OutputUntil;
pState->hSearchBuf = pState->hBoundaryBuf;
pState->next_stage = PSE_PrePart;
}
else
pState->stage = PSE_Done;
break;
case PSE_Done:
ResetPos_Buf(pInBuf); // DelPos_Buf(pInBuf);
goto Exit; // break;
case PSE_Fail:
status = RFC1847_FAIL;
goto Exit;
/* ----- ----- */
case PSE_KeepUntil:
case PSE_IgnoreUntil:
case PSE_OutputUntil:
{
Boolean bFound = false;
unsigned long nPrefixCount = 0, nAfterSkip = 0, nInLen, nSearchLen, nNewMatched;
ResetPos_Buf(pState->hSearchBuf);
nInLen = PosLen_Buf(pInBuf);
nSearchLen = PosLen_Buf(pState->hSearchBuf);
/* Did we have a partial span at the end of last buffer? */
if (pState->leftSpanCount > 0) {
//unsigned long nRemainingCount = nSearchLen - (pState->leftSpanCount);
/* Check for completion of span */
// If doesn't continue match, returns zero
// otherwise returns number of chars of hSearchBuf that have been matched
nNewMatched = CompleteCount_Buf(pInBuf, pState->hSearchBuf, pState->leftSpanCount);
if (nNewMatched == nSearchLen) { /* complete match made */
bFound = true;
nPrefixCount = 0; /* No chars in this buffer are before the match */
nAfterSkip = nSearchLen - (pState->leftSpanCount); /* Move buffer position to AFTER match */
pState->leftSpanCount = 0;
}
else if (nNewMatched == 0) { /* match failed */
/* need to 'do' pState->leftSpanCount of pState->hSearchBuf */
switch (pState->stage) {
case PSE_KeepUntil:
BufNCat_Buf(pState->hPrevBuf, pState->hSearchBuf, pState->leftSpanCount);
break;
case PSE_IgnoreUntil:
/* Ignore -- do nothing */
break;
case PSE_OutputUntil:
pState->hOutputBuf = pState->hSearchBuf;
pState->outputCount = pState->leftSpanCount;
pState->afterout_stage = pState->stage; /* Stay in same stage */
pState->stage = PSE_DoOutput;
break;
}
pState->leftSpanCount = 0;
if (pState->stage == PSE_DoOutput) {
bForceJumpState = true;
continue;
}
}
else { /* Continued to match, but not completed yet -- */
/* the input buffer is smaller than the hSearchBuf */
// Remainder of pInBuf matches, so we need to gobble it
ResetPos_Buf(pInBuf); // DelPos_Buf(pInBuf);
pState->leftSpanCount = nNewMatched;
continue; // We want to 'return', because we are done with input
}
}
ResetPos_Buf(pState->hSearchBuf);
if (!bFound) {
if (pState->hSearchBuf) { // nSearchLen?
unsigned long skip, remain;
// Find match of hSearchBuf, either complete or end-spanning
// return number of chars to skip before match
skip = SkipCount_Buf(pInBuf, pState->hSearchBuf);
remain = nInLen - skip;
if (remain > nSearchLen) { /* Found 'complete' */
bFound = true;
nPrefixCount = skip;
nAfterSkip = nSearchLen;
}
else { /* Either not found or partial possible */
nPrefixCount = skip;
nAfterSkip = remain; /* Gobble up the remaining (known to match) */
pState->leftSpanCount = remain;
}
}
else {
nPrefixCount = nInLen;
nAfterSkip = 0;
}
}
ResetPos_Buf(pState->hSearchBuf);
if (bFound) { /* Found */
switch (pState->stage) {
case PSE_KeepUntil:
BufNCat_Buf(pState->hPrevBuf, pInBuf, nPrefixCount + nAfterSkip);
pState->stage = pState->next_stage;
break;
case PSE_IgnoreUntil: /* Ignore -- do nothing */
IncPos_Buf(pInBuf, nPrefixCount + nAfterSkip);
pState->stage = pState->next_stage;
break;
case PSE_OutputUntil:
pState->hOutputBuf = pInBuf;
pState->outputCount = nPrefixCount;
pState->afterout_stage = pState->next_stage;
pState->afteroutSkip = nAfterSkip;
pState->stage = PSE_DoOutput;
break;
default:
pState->stage = PSE_Fail;
}
}
else { /* not found */
switch (pState->stage) {
case PSE_KeepUntil:
BufNCat_Buf(pState->hPrevBuf, pInBuf, nPrefixCount + nAfterSkip);
break;
case PSE_IgnoreUntil: /* Ignore -- do nothing */
IncPos_Buf(pInBuf, nPrefixCount + nAfterSkip);
break;
case PSE_OutputUntil:
pState->hOutputBuf = pInBuf;
pState->outputCount = nPrefixCount;
pState->afterout_stage = pState->stage; /* Same stage */
pState->afteroutSkip = nAfterSkip;
pState->stage = PSE_DoOutput;
break;
default:
pState->stage = PSE_Fail;
}
}
}
break;
case PSE_DoOutput:
// Need to distiguish between the two output paths
p = nil;
switch (pState->current_part) {
case 1:
p = pOutPart1;
break;
case 2:
p = pOutPart2;
break;
default:
pState->stage = PSE_Fail;
}
if (p) {
nOut = BufNIns_Buf(p, pState->hOutputBuf, pState->outputCount);
// Need to check if not everything was outputted
// If not -- buffer is full, adjust 'outputCount', return BUFFERFULL;
pState->outputCount -= nOut;
if ((pState->outputCount) > 0) { /* Anything left to output */
status = RFC1847_BUFFERFULL;
goto Exit;
}
pState->stage = pState->afterout_stage;
if (pState->afteroutSkip > 0)
IncPos_Buf(pState->hOutputBuf, pState->afteroutSkip);
}
else {
status = RFC1847_BUFFERFULL; /* No output buffer */
goto Exit;
}
break;
}
}
Exit:
HUnlock((Handle) hState);
return status;
}